<template>
    <el-input v-model="displayValue" :placeholder="placeholder" :max="max" :min="min" @input="onInput" @change="onChange" @focus="onFocus" @blur="onBlur" v-bind="$attrs">
        <template #prepend v-if="!Gx.IsEmpty(prepend)">{{ prepend }}</template>
        <template #append v-if="!Gx.IsEmpty(append)">{{ append }}</template>
    </el-input>
</template>

<script lang="ts" setup>
import Gx from "@/gx/Gx";

const CHANGE_EVENT = "change";
const INPUT_EVENT = "input";
const UPDATE_MODEL_EVENT = "update:modelValue";
const FOCUS_EVENT = "focus";
const BLUR_EVENT = "blur";

/**
 * 仿照el-input-number写的，里面的方法、接口等在el-input-number中都能找到
 */

const props = defineProps({
    modelValue: Number,

    placeholder: {
        type: String,
        default: "",
    },

    min: {
        type: [Number, null],
        default: Number.NEGATIVE_INFINITY,
    },

    max: {
        type: [Number, null],
        default: Number.POSITIVE_INFINITY,
    },

    precision: {
        type: Number,
        validator: (val: number) => val >= 0 && val === Number.parseInt(`${val}`, 10),
        default: 2,
    },

    //前缀内容
    prepend: {
        type: String,
        default: "",
    },

    //追加内容
    append: {
        type: String,
        default: "",
    },

    //当设置有精度时，是否显示小数中多余的0
    hasZero: {
        type: Boolean,
        default: false,
    },
});

interface Data {
    currentValue: number | null | undefined;
    userInput: null | number | string;
}
const data = reactive<Data>({
    currentValue: props.modelValue,
    userInput: null,
});

//显示值，用于绑定到el-input上的v-model
const displayValue = computed(() => {
    if (data.userInput !== null) {
        return data.userInput;
    }

    let currentValue: number | string | undefined | null = data.currentValue;

    if (Gx.IsEmpty(currentValue)) {
        return "";
    }

    if (Gx.IsNumber(currentValue.toString())) {
        if (Number.isNaN(currentValue)) {
            return "";
        }

        if (!Gx.IsEmpty(props.precision)) {
            if (props.hasZero) {
                currentValue = currentValue.toFixed(props.precision);
            }
        }
    }

    return currentValue;
});

//转换精度
const toPrecision = (num: number, precision?: number) => {
    if (precision === 0) {
        return Math.round(num);
    }

    let snum = String(num);
    const pointPos = snum.indexOf(".");
    if (pointPos === -1) {
        return num;
    }

    const nums = snum.replace(".", "").split("");
    const datum = nums[pointPos + precision];
    if (!datum) {
        return num;
    }

    const length = snum.length;
    if (snum.charAt(length - 1) === "5") {
        snum = `${snum.slice(0, Math.max(0, length - 1))}6`;
    }

    return Number.parseFloat(Number(snum).toFixed(precision));
};

//验证与转换数值
const verifyValue = (value: number | string | null | undefined, update?: boolean): number | null | undefined => {
    const { max, min, precision } = props;
    if (max < min) {
        throw new Error("max的值需要大于min的值！");
    }
    let newVal = Number(value);

    if (Gx.IsEmpty(value) || Number.isNaN(newVal)) {
        return null;
    }

    if (Gx.IsEmpty(value)) {
        return null;
    }

    if (!Gx.IsEmpty(props.precision)) {
        newVal = toPrecision(newVal, precision);
    }

    if (newVal > max || newVal < min) {
        newVal = newVal > max ? max : min;
        update && emits(UPDATE_MODEL_EVENT, newVal);
    }

    return newVal;
};

//设置值
const setCurrentValue = (value: number | string | null | undefined, emitChange = true) => {
    const oldVal = data.currentValue;
    const newVal = verifyValue(value);
    if (!emitChange) {
        emits(UPDATE_MODEL_EVENT, newVal!);
        return;
    }

    if (oldVal === newVal) {
        return;
    }

    data.userInput = null;
    emits(UPDATE_MODEL_EVENT, newVal!);
    emits(CHANGE_EVENT, newVal!, oldVal!);

    data.currentValue = newVal;
};

//事件--每次输入时
const onInput = (value: string) => {
    var val = verifyValue(value);

    if (value.length > 0 && val == null) {
        return;
    }

    data.userInput = value;
    const newVal = value === "" ? null : Number(value);
    emits(INPUT_EVENT, newVal);
    setCurrentValue(newVal, false);
};

//事件--失去焦点改变时
const onChange = (value: string) => {
    const newVal = value !== "" ? Number(value) : "";
    if ((Gx.IsNumber(newVal?.toString()) && !Number.isNaN(newVal)) || value === "") {
        setCurrentValue(newVal);
    }
    data.userInput = null;
};

//事件--获取焦点
const onFocus = (event: MouseEvent | FocusEvent) => {
    emits(FOCUS_EVENT, event, data.currentValue);
};

//事件--失去焦点
const onBlur = (event: MouseEvent | FocusEvent) => {
    emits(BLUR_EVENT, event, data.currentValue);
};

watch(
    () => props.modelValue,
    (value) => {
        const userInput = verifyValue(data.userInput);
        const newValue = verifyValue(value, true);
        if (!Gx.IsNumber(userInput?.toString()) && (!userInput || userInput !== newValue)) {
            data.currentValue = newValue;
            data.userInput = null;
        }
    },
    { immediate: true }
);

onMounted(() => {
    const { modelValue } = props;

    if (!Gx.IsNumber(modelValue?.toString()) && modelValue != null) {
        let val: number | null = Number(modelValue);
        if (Number.isNaN(val)) {
            val = null;
        }

        emits(UPDATE_MODEL_EVENT, val!);
    }
});

const emits = defineEmits([UPDATE_MODEL_EVENT, INPUT_EVENT, CHANGE_EVENT, FOCUS_EVENT, BLUR_EVENT]);
</script>
